/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 *  This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: iaom <zhangpengfei@kylinos.cn>
 */

#include "applications-settings.h"
#include <mutex>
#include <QQmlEngine>
#include <QDebug>
#include "settings-manager.h"
#include <QJsonObject>

using namespace UkuiNotification;
static std::once_flag flag;
static ApplicationsSettings *s_self;
namespace UkuiNotification{
class ApplicationsSettingsPrivate
{
    friend class ApplicationsSettings;
private:
    ApplicationsSettingsMap m_cache;
    QJsonObject m_settingsData;
};
}

ApplicationsSettings *UkuiNotification::ApplicationsSettings::self()
{
    std::call_once(flag, [ & ] {
        s_self = new ApplicationsSettings();
    });
    return s_self;
}

ApplicationsSettings::ApplicationsSettings(QObject *parent) : QObject(parent), d(new ApplicationsSettingsPrivate)
{
    d->m_settingsData = SettingsManager::self()->getAllAppSettings();
    connect(SettingsManager::self(), &SettingsManager::appUninstalled, this, &ApplicationsSettings::onApplicationUninstalled);
    connect(SettingsManager::self(), &SettingsManager::settingsDataChanged, this, &ApplicationsSettings::settingsDataChanged);
    qmlRegisterUncreatableType<UkuiNotification::SingleApplicationSettings>("org.ukui.notification.client", 1, 0, "SingleApplicationSettings", "");
}

ApplicationsSettings::~ApplicationsSettings()
{
    if(d) {
        qDeleteAll(d->m_cache);
        d->m_cache.clear();
        delete d;
        d = nullptr;
    }
}

SingleApplicationSettings *ApplicationsSettings::creatSettings(const PopupNotification &notification)
{
    return creatSettings(notification.desktopEntry());
}

ApplicationsSettingsMap &ApplicationsSettings::getAllApplicationsSettings()
{
    for(const QString &desktopEntry : d->m_settingsData.keys()) {
        if(!d->m_cache.contains(desktopEntry)) {
            d->m_cache.insert(desktopEntry, new SingleApplicationSettings(desktopEntry));
        }
    }
    return d->m_cache;
}

QJsonObject ApplicationsSettings::getAppSettings(const QString &appDesktopName)
{
    if(appDesktopName == QStringLiteral("default") || appDesktopName.isEmpty()) {
        return SettingsManager::self()->getAppDefaultSettings();
    }
    QJsonValue value = d->m_settingsData.value(appDesktopName);
    if(value.isUndefined()) {
        return SettingsManager::self()->getAppDefaultSettings();;
    }
    return value.toObject();
}

bool ApplicationsSettings::setAppSetting(const QString &appDesktopName, SettingsProperty::Property key,
                                         const QVariant &value)
{
    return SettingsManager::self()->setAppSettings(appDesktopName, key, value);
}

bool ApplicationsSettings::hasAppSettings(const QString &appDesktopName)
{
    return SettingsManager::self()->hasAppSettings(appDesktopName);
}

bool ApplicationsSettings::hasAppSettings(const PopupNotification &notification)
{
    return hasAppSettings(notification.desktopEntry());
}

bool ApplicationsSettings::isInAllowList(const QString &appDesktopName)
{
    return SettingsManager::self()->isInAllowList(appDesktopName);
}

bool ApplicationsSettings::isInAllowList(const PopupNotification &notification)
{
    return SettingsManager::self()->isInAllowList(notification.desktopEntry());
}

void ApplicationsSettings::settingsDataChanged()
{
    QJsonObject data = SettingsManager::self()->getAllAppSettings();
    if(d->m_settingsData == data) {
        return;
    }

    //更新现有app 配置
    for (const QString &desktopEntry : data.keys()) {
        if (d->m_settingsData.contains(desktopEntry)) {
            const QJsonValue&& oldValue = d->m_settingsData.value(desktopEntry);
            const QJsonValue&& newValue = data.value(desktopEntry);
            if ( oldValue != newValue) {
                d->m_settingsData.insert(desktopEntry, newValue);
                Q_EMIT settingsUpdated(desktopEntry);
            }
        }
    }

    //求差集
    auto diff = [](const QStringList& a, const QStringList& b) -> QStringList {
        QStringList diffList;
        diffList = (QSet<QString>(a.begin(), a.end()) - QSet<QString>(b.begin(), b.end())).values();
        return diffList;
    };

    //新增
    QStringList needAddList = diff(data.keys(), d->m_settingsData.keys());
    //删除
    QStringList needRemoveList = diff(d->m_settingsData.keys(), data.keys());

    for(const QString &desktopEntry : needAddList) {
        d->m_settingsData.insert(desktopEntry, data.value(desktopEntry));
        Q_EMIT applicationInstalled(desktopEntry);
    }

    for(const QString &desktopEntry : needRemoveList) {
        d->m_settingsData.remove(desktopEntry);
        Q_EMIT applicationUninstalled(desktopEntry);

        auto* singleSetting = d->m_cache.take(desktopEntry);
        if (singleSetting) {
            delete singleSetting;
        }
    }
}

void ApplicationsSettings::onApplicationUninstalled(const QString &desktopEntry)
{
    if (d->m_settingsData.contains(desktopEntry)) {
        d->m_settingsData.remove(desktopEntry);
        Q_EMIT applicationUninstalled(desktopEntry);

        auto* singleSetting = d->m_cache.take(desktopEntry);
        if (singleSetting) {
            delete singleSetting;
        }
    }
}

SingleApplicationSettings *ApplicationsSettings::creatSettings(QString desktopEntry)
{
    if(desktopEntry.isEmpty()) {
        desktopEntry = QStringLiteral("default");
    }
    if(d->m_cache.contains(desktopEntry)) {
        return d->m_cache[desktopEntry];
    } else {
        return d->m_cache.insert(desktopEntry, new SingleApplicationSettings(desktopEntry)).value();
    }
}
